function Checkerboard(cbid, size, camp, ws) {
	/*camp: "white" or "black" */
	this.cbid = cbid;
	this.size = size;
	this.camp = camp;
    this.wsocket = ws;
	this.ground = null;
    this.userConfirmedBattleResult = true;
	
	this.rollbackReq = JSON.stringify({
		type:"requestRollback",
		cmp:this.camp
	});

	this.confirmReq = JSON.stringify({
		type:"playerConfirm"
	});

	this.requestGo = function(i, j) {
        if(!this.userConfirmedBattleResult) return;
		var msg = JSON.stringify({
			type:"requestGo",
			row:i,
			col:j,
			cmp:this.camp
		});
		this.wsocket.send(msg);
	};

    this.sendConfirmed = function() {
    		if(!connected) return;
    		this.wsocket.send(this.confirmReq);
    	};

	this.setUserConfirmed = function(confirmed) {
		this.userConfirmedBattleResult = confirmed;
		if(confirmed) {
			this.sendConfirmed();
			this.clean();
		}
	};
    
	this.getUserConfirmed = function() {
		return this.userConfirmedBattleResult;
	};
	
	this.removeTip = function() {
		$(".pTip").removeClass("pTip");
		//$(".showTip").removeClass("showTip");
	};

	this.tip = function(i, j) {
		$("#pieceTip_" + i + "_" + j).addClass("pTip");
		//$("#piece_" + i + "_" + j).addClass("showTip");
	};
	
	this.updateTip = function(i, j) {
		this.removeTip();
		this.tip(i, j);
	};

	this.go = function(i, j, theCamp) {
		console.log("go " + i + "," + j + " : " + theCamp);

		var ijStr = i + "_" + j;
		
		$("#" + "piece_" + ijStr).addClass("isDown");

		/*
		if(this.prei != -1  &&  this.prej != -1)
			$("#pieceTip_" + this.prei + "_" + this.prej).removeClass("pTip");
		*/
		this.updateTip(i, j);
		
		var pInner = $("#" + "pieceInner_" + ijStr);
		pInner.removeClass("black");
		pInner.removeClass("white");
		pInner.addClass(theCamp);
		pInner.addClass("innerDown");
	};

	this.showWinBy = function(winBy) {
		for(ij of winBy) {
			this.tip(ij[0], ij[1]);
		}
	};
	
	this.remove = function(i, j) {
		var pInner = $("#pieceInner_" + i + "_" + j);
		$("#piece_" + i + "_" + j).removeClass("isDown");
		$("#pieceTip_" + i + "_" + j).removeClass("pTip");
		pInner.removeClass("innerDown");
		pInner.removeClass("white");
		pInner.removeClass("black");
		pInner.addClass(this.camp);
	};

	this.requestRollback = function() {
		console.log("requestRollback: " + this.rollbackReq);
		this.wsocket.send(this.rollbackReq);
	};
	
	this.rollback = function(i, j, ii, jj) {
		console.log("doRollback");
		this.remove(i, j);
		if(ii != -1 && jj != -1) {
			this.tip(ii, jj);
		}
	};
	
	this.setCamp = function(theCamp) {
		this.camp = theCamp;
	};

	this.clean = function() {
		for(var i = 0; i < this.size; ++i) {
			for(var j = 0; j < this.size; ++j) {
				this.remove(i, j);
			}
		}
	};
	
	this.getEmpty = function() {
		var ckboard = $("<div id=\"checkerboard_" + this.cbid + "\" class=\"checkerboard\">");
		
		var echHr = null;
		for(var i = 0; i < this.size; ++i) {
			echHr = $("<div class=\"btgHr\">");
			for(var j = 0; j < this.size; ++j) {
				if(i == j && i == ((this.size) >> 1)) {
					echHr.append($("<div class=\"btgVr\"><div class=\"centerPoint\"></div><div id=\"piece_" + i + "_" + j + 
							   "\" class=\"pieces\" onclick=\"" + this.cbid + ".requestGo(" + i + "," + j + ",'" + this.camp + "'" +
							   ")\"><div id=\"pieceInner_" + i + "_" + j + "\" class=\"pieceInner " + this.camp + "\"><div id=\"pieceTip_" + i + "_" + j + "\" class=\"pieceTip\"></div></div></div></div>"));
				} else {
					echHr.append($("<div class=\"btgVr\"><div id=\"piece_" + i + "_" + j + 
							   "\" class=\"pieces\" onclick=\"" + this.cbid + ".requestGo(" + i + "," + j + ",'" + this.camp + "'" +
							   ")\"><div id=\"pieceInner_" + i + "_" + j + "\" class=\"pieceInner " + this.camp + "\"><div id=\"pieceTip_" + i + "_" + j + "\" class=\"pieceTip\"></div></div></div></div>"));
				}
			}
			echHr.append($("<div class=\"btgVr\"></div>"));
			
			ckboard.append(echHr);
		}
	
		echHr = $("<div class=\"btgHr\">");
		for(var j = 0; j <= this.size; ++j) {
			echHr.append($("<div class=\"btgVr\"></div>"));
		}
		
		ckboard.append(echHr);
		this.ground = ckboard;
		return ckboard;
	};
}
